﻿using Demo.AttachFileService.Services;
using JT808.Protocol.Extensions.JTActiveSafety.MessageBody;
using JT808.Protocol.Extensions.JTActiveSafety.Metadata;
using JTActiveSafety.Gateway;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Demo.AttachFileService.Models
{
    public class SqliteDbService : ISqliteDbService
    {
        private readonly ILogger _logger;
        //private readonly SqliteDbContext _dataContext; 
        private readonly SqliteConnection _connection = new SqliteConnection("Data Source=:memory:");


        public SqliteDbService(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<SqliteDbService>();
            _connection.Open();
            CreateTempTable();
        }


        public void CreateTempTable()
        {
            var createCommand = _connection.CreateCommand();
            createCommand.CommandText = $"CREATE TABLE data (id INTEGER PRIMARY KEY,value BLOB)";
            createCommand.ExecuteNonQuery();
        }
        public void CreateTempTable2()
        {
            var createCommand = _connection.CreateCommand();
            createCommand.CommandText = File.ReadAllText("input.sql");
            createCommand.ExecuteNonQuery();
        }


        private void WriteData(int id, byte[] body)
        {
            //var tmp = File.Open("F:\\AttachFile\\tmp\\" + id, FileMode.Create);
            //tmp.Write(body, 0, body.Length);
            var insertCommand = _connection.CreateCommand();
            insertCommand.CommandText = $"INSERT INTO data(id,value) VALUES ($id,zeroblob($length));" +
                $"SELECT last_insert_rowid();";
            insertCommand.Parameters.AddWithValue("$id", id);
            insertCommand.Parameters.AddWithValue("$length", body.Length);
            var rowid = (long)insertCommand.ExecuteScalar();

            using (var writeStream = new SqliteBlob(_connection, "data", "value", rowid))
            {
                writeStream.Write(body, 0, body.Length);
            }
        }

        public byte[] ReadData(int id)
        {
            //if (File.Exists("F:\\AttachFile\\tmp\\" + id))
            //   return File.ReadAllBytes("F:\\AttachFile\\tmp\\" + id);
            //return null;
            var selectCommand = _connection.CreateCommand();
            selectCommand.CommandText = $"SELECT value FROM data WHERE id={id} LIMIT 1";
            using (var reader = selectCommand.ExecuteReader())
            {
                while (reader.Read())
                {
                    //var writer = new BinaryWriter(reader.GetStream(1));
                    var r = new BinaryReader(reader.GetStream(0));
                    return r.ReadBytes((int)r.BaseStream.Length);
                    //using (var readStream = reader.GetStream(1))
                    //{
                    //    await readStream.CopyToAsync(outputStream);
                    //}
                }
            }
            return null;
        }

        public void DeleteData(int id)
        {
            //if (File.Exists("F:\\AttachFile\\tmp\\" + id))
            //    File.Delete("F:\\AttachFile\\tmp\\" + id);
            var createCommand = _connection.CreateCommand();
            createCommand.CommandText = $"DELETE FROM data WHERE id = {id}";
            createCommand.ExecuteNonQuery();
        }

        /// <summary>
        /// 创建报警信息并同步添加关联的附件文件集合
        /// </summary>
        /// <param name="terminalPhoneNo"></param>
        /// <param name="input"></param>
        public void CreateAlarmInfoAndAttachFiles(string terminalPhoneNo, JT808_0x1210 input)
        {
            using var _dataContext = new SqliteDbContext();
            var tmp = _dataContext.AlarmInfos.Any(c => c.AlarmId == input.AlarmId);
            if (tmp == false)
                _dataContext.AlarmInfos.Add(new AlarmInfo()
                {
                    AlarmId = input.AlarmId,
                    AlarmNo = input.AlarmIdentification.ToString(),
                    AlarmTime = input.AlarmIdentification.Time,
                    AttachCount = input.AttachCount,
                    SN = input.AlarmIdentification.SN,
                    TerminalPhoneNo = terminalPhoneNo
                });

            var tmp2 = _dataContext.AttachFiles.Any(c => c.AlarmId == input.AlarmId);
            if (tmp2 == false)
                _dataContext.AttachFiles.AddRange(input.AttachInfos.Select(c => new AttachFile()
                {
                    FileName = c.FileName,
                    FileSize = c.FileSize,
                    AlarmId = input.AlarmId.ToString()
                }).ToList());
            try
            {
                if (!tmp || tmp2)
                    _dataContext.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                _logger.LogError(ex, $"0x1210 AlarmId：{input.AlarmId}");
                return;
            }
        }

        public void AddFileRecvRecored(JT808_0x1299 jT808_0X1299)
        {
            using var _dataContext = new SqliteDbContext();
            _dataContext.FileRecvRecords.Add(new FileRecvRecored()
            {
                Offset = jT808_0X1299.Offset,
                Length = jT808_0X1299.Length,
                FileName = jT808_0X1299.FileName
            });
            try
            {
                _dataContext.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                _logger.LogError(ex, $"0x1211_2 FileName：{jT808_0X1299.FileName} Offset-{jT808_0X1299.Offset} length-{jT808_0X1299.Length}");
                return;
            }
        }

        public List<FileRecvRecored> WriteData(FileRecvRecored recored)
        {
            //var _dataContext = new SqliteDbContext();
            //var fileSize = _dataContext.QueryAttachFiles.Where(c => c.FileName == recored.FileName)
            //    .OrderBy(c => c.Id)
            //    .LastOrDefault()?.FileSize ?? 0u;

            var body = recored.Bodies;
            recored.Bodies = null;
            using var _dataContext = new SqliteDbContext();

            var tmp = _dataContext.FileRecvRecords.FirstOrDefault(c =>
            c.FileName == recored.FileName && 
            c.Offset == recored.Offset && 
            c.Length == recored.Length);

            if (tmp == null)
            {
                _dataContext.FileRecvRecords.Add(recored);
                try
                {
                    _dataContext.SaveChanges();
                }
                catch (DbUpdateConcurrencyException ex)
                {
                    _logger.LogError(ex, $"写入附件文件数据块错误：{recored.Offset} {recored.Length}");
                }
            }

            var x = CheckFileIntegrity(recored.FileName, out var fileSize, out var nodes);
            if (x == CheckFileIntegrityResult.检查成功_文件不完整)
            {
                WriteData(recored.Id, body);
            }
            else if (x == CheckFileIntegrityResult.检查成功_文件完整)
            {
                return _dataContext.FileRecvRecords.Where(c => c.FileName == recored.FileName).OrderBy(c => c.Offset).ToList();
            }

            return null;
        }

        public void HandlerEnd(string fileName)
        {
            using var _dataContext = new SqliteDbContext();
            var file = _dataContext.QueryAttachFiles.Where(c => c.FileName == fileName).OrderByDescending(c => c.Id).FirstOrDefault();
            if (file != null)
            {
                file.IsOk = true;
                _dataContext.AttachFiles.Update(file);
            }
            try
            {
                _dataContext.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                _logger.LogError(ex, $"更新附件文件已全部接收、处理完成标记时出错：{fileName}");
            }
        }

        public CheckFileIntegrityResult CheckFileIntegrity(string fileName, out uint fileSize, out List<Node> missingNodes, bool resultMissing = false)
        {
            fileSize = 0;
            missingNodes = new List<Node>();
            using var _dataContext = new SqliteDbContext();
            var file = _dataContext.QueryAttachFiles.Where(c => c.FileName == fileName).OrderByDescending(c => c.Id).FirstOrDefault();//?.FileSize ?? 0u;
            if (file == null)
            {
                return CheckFileIntegrityResult.找不到对应文件记录;
            }
            fileSize = file.FileSize;
            if (file.IsOk)
            {
                return CheckFileIntegrityResult.文件已存储成功;
            }

            var nodes = _dataContext.FileRecvRecords.Where(c => c.FileName == fileName)
                .Select(c => new Node { Offset = c.Offset, Length = c.Length }).OrderBy(c => c.Offset).ToList();

            if (nodes == null || nodes.Count == 0)
            {
                return CheckFileIntegrityResult.打不到文件数据包记录;
            }
            else if (nodes.Count > 0)
            {
                if (resultMissing)
                    if (nodes.Sum(c => c.Length) < fileSize)
                    {
                        return CheckFileIntegrityResult.检查成功_文件不完整;
                    }

                if (CheckLinkedListMissingNodes.TryCheck(nodes, fileSize, ref missingNodes))
                {
                    if (missingNodes.Count == 0)
                    {
                        return CheckFileIntegrityResult.检查成功_文件完整;
                    }
                    return CheckFileIntegrityResult.检查成功_文件不完整;
                }
                _logger.LogWarning($"Check File Integrity：{fileName} size-{fileSize}");
                return CheckFileIntegrityResult.数据错误;
            }
            return CheckFileIntegrityResult.数据错误;
        }

    }
}
